package boot.example.tcp.server.netty;

import boot.example.tcp.server.model.EchoResponse;
import boot.example.tcp.server.model.TUser;
import boot.example.tcp.server.service.MyService;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 数据处理器，针对不同类型数据分类处理 在处理不同接收数据时使用了枚举类型(回调)
 *
 * @Description BootNettyChannelInboundHandlerAdapter
 * @Author guoyb
 * @Date 2023/9/28 14:39
 */
@ChannelHandler.Sharable
@Slf4j
public class NettyServerHandler extends SimpleChannelInboundHandler<String> {

    /**
     * 管理一个全局map，保存连接进服务端的通道数量
     */
    public static Map<String, ChannelHandlerContext> map = new HashMap<String, ChannelHandlerContext>();

    //region 使用依赖注入
    private static NettyServerHandler nettyServerHandler;

    @Autowired
    MyService myService;

    public NettyServerHandler() {

    }

    @PostConstruct
    public void init() {
        nettyServerHandler = this;
    }

    // 调用时：nettyServerHandler.myService.analysis(msgByte);


    /**
     * 注册时执行
     */
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
        log.info("有客户端连接--channelRegistered--" + ctx.channel().id().toString());
    }

    /**
     * 离线时执行
     */
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
        log.info("--channelUnregistered--" + ctx.channel().id().toString());
    }

    /**
     * 从客户端收到新的数据时，这个方法会在收到消息时被调用
     */
    @Override
    public void messageReceived(ChannelHandlerContext ctx, String msg) {
        try {
            if (msg == null) {
                return;
            }
            String channelId = ctx.channel().id().toString();

            //因为我们使用了 String 编解码器，所以不会是 ByteBuf，字节信息
            System.out.println("接收到客户端(" + ctx.channel().remoteAddress() + ")消息 ->" + msg);
            log.info("channelId=" + channelId + "data=" + msg);


            // 接收到的对象的类型为EchoRequest
            // EchoRequest req = JSON(msg, EchoRequest.class);
            // log.info(req.getRequestId() + " : " + req.getRequestObj());

            // 创建需要传输的user对象
            TUser user = new TUser();
            user.setName("server222");
            user.setAge(20);
            // 创建传输的user对象载体EchoRequest对象
            EchoResponse resp = new EchoResponse();
            // 设置responseId
            resp.setResponseId(UUID.randomUUID().toString());
            // 设置需要传输的对象
            resp.setResponseObj(user);
            // 设置需要传输的对象的类型
            // 调用writeAndFlush将数据发送到socketChannel
            // ctx.writeAndFlush(resp);
            // ctx.writeAndFlush(Unpooled.buffer().writeBytes(Unpooled.copiedBuffer("112aaa阿道夫", CharsetUtil.UTF_8)));
            // ctx.writeAndFlush(Unpooled.buffer().writeBytes(("writeBytes:" + channelId).getBytes()));
            ctx.writeAndFlush(Unpooled.copiedBuffer("ok", CharsetUtil.UTF_8));


        } catch (Exception e) {
            // 抛弃收到的数据
            ReferenceCountUtil.release(msg);
            e.printStackTrace();
            log.error("异常信息:{}", e.getMessage());
        }
    }


    /**
     * 从客户端收到新的数据、读取完成时调用
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException {
        System.out.println("channelReadComplete");
        ctx.flush();
    }

    /**
     * 当出现 Throwable 对象才会被调用，即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws IOException {
        System.out.println("exceptionCaught");
        cause.printStackTrace();
        BootNettyChannel bootNettyChannel = BootNettyChannelCache.get("server:" + ctx.channel().id().toString());
        if (bootNettyChannel != null) {
            BootNettyChannelCache.remove("server:" + ctx.channel().id().toString());
        }
        ctx.close();//抛出异常，断开与客户端的连接
    }

    /**
     * 客户端与服务端第一次建立连接时 执行
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelActive(ctx);
        ctx.channel().read();
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
        //此处不能使用ctx.close()，否则客户端始终无法与服务端建立连接
        System.out.println("channelActive:" + clientIp + ctx.name());
    }

    /**
     * 客户端与服务端 断连时 执行
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelInactive(ctx);
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
        System.out.println("channelInactive:" + clientIp);
        BootNettyChannel bootNettyChannel = BootNettyChannelCache.get("server:" + ctx.channel().id().toString());
        if (bootNettyChannel != null) {
            BootNettyChannelCache.remove("server:" + ctx.channel().id().toString());
        }
        ctx.close(); //断开连接时，必须关闭，否则造成资源浪费，并发量很大情况下可能造成宕机
    }

    /**
     * 服务端当read超时, 会调用这个方法
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception, IOException {
        super.userEventTriggered(ctx, evt);
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();

        ctx.close();//超时时断开连接
        System.out.println("userEventTriggered:" + clientIp);
    }

}
